/**************************************************************

Project:     L-Spider
Description: Laser proximity control system

MCU: PIC16C628A
Clock: Internal (48kHz)

Config word:
       - Watchdog Timer:   Disabled
       - Power-On Reset:   Enabled
       - Master Clear:     Enabled
       - Brown-out Detect: Disabled
       - Low Voltage Prog: Disabled
       - Data EE Protect:  Disabled
       - Oscillator:       INTOSC
       
***************************************************************/

// Includes

#include <device-timer.c>
#include <beam-timer.c>

//----------------------------------------------------------------

// Constants

#define BEAM_TIMER_THRESHOLD 20
#define DEVICE_TIMER_THRESHOLD 0

#define BEAM_ON 1
#define BEAM_OFF 0

#define DEVICE_PRIMARY 1
#define DEVICE_SECONDARY 2

//----------------------------------------------------------------

// Global identifiers

unsigned char current_beam_state;
unsigned char previous_beam_state;
unsigned int n_alarms;

//*****************************************************************

void mcu_initialize() {

  // Initialize peripherals

    T1CON = 0b00110000;   // Shut off TMR1; internal clock, set prescaler to 1:8
    T2CON = 0b00000011;   // Shut off TMR2; set prescaler to 1:16
    
    CCP1CON = 0;          // Shut off CCP/PWM
    RCSTA = 0;            // Disable USART

    CMCON = 0b00000010;   // Put comparators into common internal reference mode,
                          //   C2Out not inverted
                        
    VRCON = 0b10001111;   // Reference voltage circuit on, hi range, scaler = 15,
                          //   Vref output on RA2 off
    
    PCON  = 0b00000000;      // Set internal oscillator frequency to low (48 kHz)
    INTCON = 0b00000000;     // Disable
    PIE1 = 0;                //    all interrupts
    OPTION_REG = 0b00000111; // PORTB pull-up enabled,
                             //   TMR0 clocked by instruction cycle clock,
                             //   prescaler = 1:128 assigned to TMR0

    // Configure I/O ports

    TRISA = 0b00111111;  // Output: RA7:6 (action devices)
                         //   RA5:0 in tri-state
    TRISB = 0b11110001;  // Output: RB4:1 (RB4 = alarm LED)
                         //   Input: RB7:5,0 (7:5 = vref scaler register, 0 = alignment mode)

} // mcu_initialize()

//========================================================

unsigned char vref_scaler_read() {
    return ( PORTB & 0b11110000 ) >> 4; // Read 4-bit scaler from RB7:4 and shift to LSB
} // vref_scaler_read()

//========================================================

void vref_scaler_load( unsigned int vref_scaler) {
     vref_scaler = vref_scaler_read() | ( VRCON & 0b11110000 );
     VRCON = vref_scaler;
} // vref_scaler_read()

//========================================================

void beam_state_indicate( unsigned char beam_state ) {
    if ( beam_state == BEAM_OFF )  // If beam shaded,
        PORTB &= 0b11110111;       //   light the LED (active low)
    else
        PORTB |= 0b00001000;
} // beam_state_indicate()

//========================================================

unsigned int beam_state_query() {
   vref_scaler_load( vref_scaler_read() );  // Read and load the vref scaler value
   return ( CMCON & 0b10000000 ) >> 7;      // Will return BEAM_ON if CMP2 outputs 1,
                                            //   meaning Vin- < Vin+ (Vin- is transducer input,
                                            //   Vin+ is internal reference voltage)
} // beam_state_query()

//========================================================

void device_activate( unsigned char device_number ) {
    switch( device_number ) {
        case 1: PORTA &= 0b01111111;
                break;
        case 2: PORTA &= 0b10111111;
                break;
    } // switch device_number
} // device_activate()

//========================================================

void device_deactivate( unsigned char device_number ) {
    switch( device_number ) {
        case 1: PORTA |= 0b10000000;
                break;
        case 2: PORTA |= 0b01000000;
                break;
    } // switch device_number
} // device_deactivate()

//========================================================

unsigned char in_alignment_mode() {
    return ~( ( PORTB & 0b00000001 ) | 0b11111110);  // If bit RB0 is 0 (active), will return 1
} // in_alignment_mode()

//*****************************************************************

void main() {

    mcu_initialize(); // Initialize MCU

    // Initialize variables

    current_beam_state = BEAM_OFF;
    previous_beam_state = BEAM_OFF;
    n_alarms = 0;

    device_deactivate( DEVICE_PRIMARY );
    device_deactivate( DEVICE_SECONDARY );

    beam_timer_stop();
    beam_timer_reset();

//========================================================

    // Main working cycle

    while( 1 ) {

       current_beam_state = beam_state_query();    // Get current beam state
       beam_state_indicate( current_beam_state );  // Show beam status via alarm LED

       if ( !in_alignment_mode() ) {                            // If not in alighment mode
           if ( current_beam_state != previous_beam_state ) {   //   and beam changed its status
               previous_beam_state = current_beam_state;        // Remember new beam state
               if ( current_beam_state == BEAM_ON ) {           // If beam changed status to ON
                  beam_timer_stop();                            //   stop beam timeout counter
                  beam_timer_reset();                           //   and reset it
               } else {                                         // Else
                  beam_timer_start( BEAM_TIMER_THRESHOLD );     //   start the beam timeout timer
               }
           } // if current_beam_state
           else {                                               // If beam status did not change
               if ( beam_timer_overflow() ) {                   //   and beam timeout timer overflowed
                  n_alarms += 1;                                // Increment the number of alarms since reset
                  beam_timer_stop();                            // Stop the beam timeout timer
                  beam_timer_reset();                           //   and reset it
                  
                  device_activate( DEVICE_PRIMARY );            // Activate primary device anyway

                  if ( n_alarms > 1 ) {                         // If this is not the 1st alarm
                      device_activate( DEVICE_SECONDARY );      //   activate secondary device too
                  } // if n_alarms > 1
                  
                  device_timer_reset();                         // Reset device timer
                  device_timer_start( DEVICE_TIMER_THRESHOLD ); //   and start counting the exposition
               }
           } // else current_beam_state
       } // if !( in_alignment_mode()

//-----------------------------------------------------

       if ( device_timer_overflow() ) {              // If the device(s) working too long
           device_deactivate( DEVICE_PRIMARY );      // Deactivate
           device_deactivate( DEVICE_SECONDARY );    //   both devices
           device_timer_stop();                      // Stop device timer
           device_timer_reset();                     //   and reset it
       } // if beam_timer_overflow()
       
//-----------------------------------------------------
       
    } // while(1)
} // main()

// *** End of file ***
